package org.hackystat.ui.sensordataviewer.client;

import java.util.ArrayList;
import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;


/**
 * A widget to view realtime Hackystat sensor data.
 * @author David J. Nickles
 */
public class CompositeWidget implements EntryPoint, ClickListener, ChangeListener, FocusListener {
  /** {@literal} WAIT_IMAGE icon. */        
  public static final String WAIT_IMAGE = "images/wait-animated.gif"; 
  /** {@literal} TIMER_IMAGE event state. */  
  private static final String TIMER_IMAGE = "images/clock.gif";   
  /** {@literal} HOST_IMAGE host icon. */        
  private static final String HOST_IMAGE = "images/host.gif"; 
  /** {@literal} HOST_INVALID host invalid event state. */        
  private static final String HOST_INVALID = "images/host-warning.gif"; 
  /** {@literal} HOST_VALID host valid event state. */  
  private static final String HOST_VALID = "images/host-green.gif";  
  /** {@literal}  USER_IMAGE user icon. */
  private static final String USER_IMAGE = "images/user.gif";
  /** {@literal} USER_INVALID user invalid event state. */        
  private static final String USER_INVALID = "images/user-warning.gif"; 
  /** {@literal} USER_VALID user valid event state. */  
  private static final String USER_VALID = "images/user-green.gif";  
  /** {@literal} OPEN_BOOK journal open state. */        
  private static final String OPEN_BOOK = "images/open-book.gif"; 
  /** {@literal} CLOSED_BOOK journal closed state. */        
  private static final String CLOSED_BOOK = "images/closed-book.gif"; 
  /** {@literal} SMALLLABELSTYLE stylesheet class for labels. */  
  private static final String SMALLLABELSTYLE = "label_sm";   
  /** {@literal} SMALLLABELSTYLE stylesheet class for examples. */  
  private static final String EXAMPLELABELSTYLE = "label_example"; 
  /** {@literal} VALIDLABELSTYLE stylesheet class for valid results. */  
  private static final String VALIDLABELSTYLE = "label_valid"; 
  /** {@literal} INVALIDLABELSTYLE stylesheet class for invalid results. */  
  private static final String INVALIDLABELSTYLE = "label_invalid";
  /** {@literal} SMALLLABELSTYLE stylesheet class for examples. */  
  private static final String SETTINGSPANELSTYLE = "settingsPanel"; 
  /** {@literal} PRCERRORNAME name for RPC errors. */  
  private static final String PRCERRORNAME = "RPC client error"; 
  /** {@literal} DEFAULT default project name. */  
  private static final String DEFAULT = "Default"; 

  /* Sensorbase URI parameters */
  private Timer poller;
  private String newLineChar;
  private String prevPollTstamp;
  private String currPollTstamp;
  private String lastArrivalTime;
  private Date serverTstamp;
  /* Widget UI elements */
  private Button registerUserButton;
  private ScrollPanel scrollPanel;
  private ImageConsciousStackPanel compartments;
  private EventCollection dataTree;
  private TextBox userTextBox;
  private PasswordTextBox passwordTextBox;
  private Label hostLabel;
  private Label userLabel;
  private Label passwordLabel;
  private Label hostPanelFeedback;
  private Label userPanelFeedback;
  private HTML timerPanelFeedback;
  private HTML dataPanelFeedback;
  private ListBox pollingBox;
  private Image hostPanelImage;
  private Image userPanelImage;
  private Image timerPanelImage;
  private Image dataPanelImage;
  // New Display
  private Boolean hostStatus;
  private Boolean userStatus;
  private Date cookieExpires;
  private int lastCallTotal;
  private int total;
  private boolean rpcAvailable;
  private boolean firstCalInit;
  
  /**
   * This method sets the title in the gadget.
   * @param title to display in the iGoogle gadget title bar.
   */
  public static native void setGadgetTitle(String title) /*-{
    var ne = $wnd._nativeExtensions;
    $wnd._nativeExtensions = false;
    $wnd._IG_SetTitle(title);
    $wnd._nativeExtensions = ne;
  }-*/;

  /**
   * This method tells the gadget to resize itself.
   */
  public static native void resizeGadget() /*-{
    var ne = $wnd._nativeExtensions;
    $wnd._nativeExtensions = false;
    $wnd._IG_AdjustIFrameHeight();
    $wnd._nativeExtensions = ne;
  }-*/;
  
  /**
   * This method creates and initializes the small gadget settings panel.
   */
  private void initLayout() {
    this.hostStatus = Boolean.FALSE;
    this.userStatus = Boolean.FALSE;
    this.newLineChar = "<br />";
    this.cookieExpires = new Date((new Date()).getTime() + (1000 * 60 * 60 * 24 * 365)); //one year
    
    this.compartments = new ImageConsciousStackPanel();
    
    VerticalPanel hostSettingsPanel = new VerticalPanel();
    hostSettingsPanel.setStyleName(SETTINGSPANELSTYLE);
    HorizontalPanel row1 = new HorizontalPanel();
    Label hostLabel = new Label("URI:");
    hostLabel.setStyleName(EXAMPLELABELSTYLE);
    row1.add(hostLabel);
    this.hostLabel = new Label();
    this.hostLabel.setStyleName(EXAMPLELABELSTYLE);
    row1.add(this.hostLabel);
    hostSettingsPanel.add(row1);
    this.compartments.add(hostSettingsPanel, "<div id=\"hostPanelTitleSlot\"></div>", true);

    VerticalPanel userSettingsPanel = new VerticalPanel();
    userSettingsPanel.setStyleName(SETTINGSPANELSTYLE);
    VerticalPanel cell1row2n3 = new VerticalPanel();
    HorizontalPanel row2 = new HorizontalPanel();
    Label userLabel = new Label("E-mail:");
    userLabel.setStyleName(SMALLLABELSTYLE);
    row2.add(userLabel);
    this.userTextBox = new TextBox();
    String userCookie = Cookies.getCookie("user");
    if (userCookie != null) {
      this.userTextBox.setText(userCookie);
    }
    this.userTextBox.addFocusListener(this);
    row2.add(this.userTextBox);
    HorizontalPanel row25 = new HorizontalPanel();
    this.userLabel = new Label("Example: user@hackystat.org");
    this.userLabel.setStyleName(EXAMPLELABELSTYLE);
    row25.add(this.userLabel);
    row25.add(this.registerUserButton = new Button("Register"));
    this.registerUserButton.addClickListener(this);
    cell1row2n3.add(row2);
    cell1row2n3.add(row25);
    HorizontalPanel row3 = new HorizontalPanel();
    Label keyLabel = new Label("Password:");
    row3.add(keyLabel);
    this.passwordTextBox = new PasswordTextBox();
    String passwordCookie = Cookies.getCookie("password");
    if (passwordCookie != null) {
      this.passwordTextBox.setText(passwordCookie);
    }
    this.passwordTextBox.addFocusListener(this);
    row3.add(this.passwordTextBox);
    keyLabel.setStyleName(SMALLLABELSTYLE);
    this.passwordLabel = new Label("Example: YEr7zqHKA7eg");
    this.passwordLabel.setStyleName(EXAMPLELABELSTYLE);
    cell1row2n3.add(row3);
    cell1row2n3.add(this.passwordLabel);
    userSettingsPanel.add(cell1row2n3);
    this.compartments.add(userSettingsPanel, "<div id=\"userPanelTitleSlot\"></div>", true);
    
    VerticalPanel timerSettingsPanel = new VerticalPanel();
    timerSettingsPanel.setStyleName(SETTINGSPANELSTYLE);
    HorizontalPanel row4 = new HorizontalPanel();
    Label intervalLabel = new Label("Poll (seconds):");
    intervalLabel.setStyleName(SMALLLABELSTYLE);
    row4.add(intervalLabel);
    this.pollingBox = new ListBox();
    this.pollingBox.addItem("10");
    this.pollingBox.addItem("15");
    this.pollingBox.addItem("30");
    this.pollingBox.addItem("60");
    String intervalCookie = Cookies.getCookie("interval");
    if (intervalCookie != null) {
      this.pollingBox.setSelectedIndex(Integer.valueOf(intervalCookie).intValue());
    }
    this.pollingBox.setVisibleItemCount(1); 
    this.pollingBox.addChangeListener(this);
    row4.add(this.pollingBox);
    timerSettingsPanel.add(row4);
    this.compartments.add(timerSettingsPanel, "<div id=\"timerPanelTitleSlot\"></div>", true);

    this.dataTree = new EventCollection(this);
    this.scrollPanel = new ScrollPanel(this.dataTree);
    this.scrollPanel.setHeight("380px");
    String visibleCookie = Cookies.getCookie("data-visible");
    if (visibleCookie != null) {
      this.scrollPanel.setVisible(Boolean.valueOf(visibleCookie).booleanValue());
    }
    this.compartments.add(this.scrollPanel, "<div id=\"dataPanelTitleSlot\"></div>", true);
    
    RootPanel.get("widget").add(this.compartments);
    
    HorizontalPanel hostColumns = new HorizontalPanel();
    this.hostPanelImage = new Image(HOST_IMAGE);
    hostColumns.add(this.hostPanelImage);
    VerticalPanel hostRows = new VerticalPanel();
    Label hostLabel2 = new Label("Host");
    hostRows.add(hostLabel2);
    hostLabel2.setStyleName(SMALLLABELSTYLE);
    this.hostPanelFeedback = new Label("Set the SensorBase host address.");
    this.hostPanelFeedback.setStyleName(EXAMPLELABELSTYLE);
    hostRows.add(this.hostPanelFeedback);
    hostColumns.add(hostRows);
    RootPanel.get("hostPanelTitleSlot").add(hostColumns);
    
    HorizontalPanel userColumns = new HorizontalPanel();
    this.userPanelImage = new Image(USER_IMAGE);
    userColumns.add(this.userPanelImage);
    VerticalPanel userCol2Rows = new VerticalPanel();
    Label userPanelTitle = new Label("User");
    userCol2Rows.add(userPanelTitle);
    userPanelTitle.setStyleName(SMALLLABELSTYLE);
    this.userPanelFeedback = new Label("Set a valid host, before entering user credentials.");
    this.userPanelFeedback.setStyleName(EXAMPLELABELSTYLE);
    userCol2Rows.add(this.userPanelFeedback);
    userColumns.add(userCol2Rows);
    RootPanel.get("userPanelTitleSlot").add(userColumns);
    
    HorizontalPanel timerColumns = new HorizontalPanel();
    this.timerPanelImage = new Image(TIMER_IMAGE);
    timerColumns.add(this.timerPanelImage);
    VerticalPanel timerCol2Rows = new VerticalPanel();
    Label timerPanelTitle = new Label("Interval");
    timerCol2Rows.add(timerPanelTitle);
    timerPanelTitle.setStyleName(SMALLLABELSTYLE);
    this.timerPanelFeedback = new HTML("Polling approximately every " 
      + this.pollingBox.getValue(this.pollingBox.getSelectedIndex()) + " seconds.");
    this.timerPanelFeedback.setStyleName(EXAMPLELABELSTYLE);
    timerCol2Rows.add(this.timerPanelFeedback);
    timerColumns.add(timerCol2Rows);
    RootPanel.get("timerPanelTitleSlot").add(timerColumns);
    
    HorizontalPanel dataColumns = new HorizontalPanel();
    if (this.scrollPanel.isVisible()) {
      this.dataPanelImage = new Image(OPEN_BOOK);
    } 
    else {
      this.dataPanelImage = new Image(CLOSED_BOOK);
    }
    this.dataPanelImage.addClickListener(this);
    dataColumns.add(this.dataPanelImage);
    VerticalPanel dataCol2Rows = new VerticalPanel();
    Label dataPanelTitle = new Label("Sensor Data");
    dataCol2Rows.add(dataPanelTitle);
    dataPanelTitle.setStyleName(CompositeWidget.SMALLLABELSTYLE);
    this.dataPanelFeedback = new HTML("This viewer's data log.");
    this.dataPanelFeedback.setStyleName(EXAMPLELABELSTYLE);
    dataCol2Rows.add(this.dataPanelFeedback);
    dataColumns.add(dataCol2Rows);
    RootPanel.get("dataPanelTitleSlot").add(dataColumns);
    this.compartments.setDataPanelImage(this.dataPanelImage, OPEN_BOOK, CLOSED_BOOK);
  }
  
  /**
   * This method creates, initializes and starts the timer task. 
   * Also initializes the request window.
   */
  private void initTimer() {
    this.currPollTstamp = DateTimeFactories.makeTimestamp();
    this.prevPollTstamp = this.currPollTstamp;
    this.lastArrivalTime = "N/A";
    this.poller = new Timer() {
      public void run() {
        poll();
      }
    };
    this.poller.scheduleRepeating(
        Integer.parseInt(this.pollingBox.getValue(this.pollingBox.getSelectedIndex())) * 1000);
  }
  
  /**
   * This method displays the passed version in the UI.
   * @param version a string containing the version number 
   */
  public void setVersion(String version) {
    Window.setTitle("Hackystat: SensorDataViewer " + version);
    RootPanel.get("version").add(new HTML("<b>" + version + "</b>"));
    setGadgetTitle("Hackystat: SensorDataViewer " + version);
  }
  
  /**
   * Check with the servlet to see if the host is valid.
   */
  private void rpcVersion() {
    RPCServiceFactory.getInstance().getService().version(new AsyncCallback() {
      public void onSuccess(Object result) {
        setVersion((String) result);
      }
  
      public void onFailure(Throwable caught) {
        if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
          log(PRCERRORNAME, "Cannot invoke version request on servlet.");
        } 
        else {
          log(PRCERRORNAME, caught.toString());
        }
      }
    });
  }
  
  /**
   * This method sets the name of the host uri.
   * @param uri the sensorbase host domain and path
   */
  public void setHostUri(String uri) {
    this.hostLabel.setText(uri);
    this.rpcAvailable = true;
    this.hostPanelImage.setUrl(HOST_IMAGE);
    if (this.firstCalInit) {
      rpcIsHost();
    }
  }
  
  /**
   * Check with the servlet to get the host name from the properties file.
   */
  private void rpcGetHost() {
    if (this.rpcAvailable) {
      this.rpcAvailable = false;
      this.hostPanelImage.setUrl(WAIT_IMAGE);
      RPCServiceFactory.getInstance().getService().getHost(new AsyncCallback()
      {
        public void onSuccess(Object result) {
          setHostUri((String) result);
        }
    
        public void onFailure(Throwable caught) {
          if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
            log(PRCERRORNAME, "Cannot invoke get host uri property on servlet.");
          } 
          else {
            log(PRCERRORNAME, caught.toString());
          }
        }
      });
    }
  }

  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    this.firstCalInit = true;
    this.rpcAvailable = true;
    initLayout();
    rpcVersion();
    rpcGetHost();
    initTimer();
  }

  /**
   * This method performs an asynchronous remote call out to the sensor data viewer service
   * for particular sensor data over a specified period as indicated in the URI.
   * @param host the URI for the SensorBase
   * @param project the project of interest
   * @param user for authentication
   * @param password for authentication
   * @param startXmlTime XML-format timestamp for beginning of request interval
   * @param endXmlTime XML-format timestamp for ending of the request interval
   */ 
  public void rpcGetMod(String host, String project, String user, String password,
    String startXmlTime, String endXmlTime) { 
    if ((this.hostStatus.booleanValue()) && (this.userStatus.booleanValue())) {
      RPCServiceFactory.getInstance().getService().getMod(host, project, user, password, 
        startXmlTime, endXmlTime, new AsyncCallback()
        {
          public void onSuccess(Object result) {
            /** @gwt.typeArgs result <java.util.ArrayList<java.lang.String>> */
            work((ArrayList) result);
          }
  
          public void onFailure(Throwable caught) {
            if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
              log(PRCERRORNAME, "Cannot invoke method on servlet.");
            } 
            else {
              log(PRCERRORNAME, caught.toString());
            }
          }
        });
    }
  }

  /**
   * This method performs an asynchronous remote call out to the sensor data browser service
   * to get a list of days with data.
   * @param hrefList a list of the hrefs of the sensor data elements to get.
   * @param callback the treeItem to return the answer to.
   * 
   * @gwt.typeArgs hrefList <java.lang.String>
   */ 
  public void rpcGetHrefs(ArrayList hrefList, final Event callback) {
    if ((this.hostStatus.booleanValue()) && (this.userStatus.booleanValue())) {
      RPCServiceFactory.getInstance().getService().getHrefs(this.hostLabel.getText(), 
        this.userTextBox.getText(), this.passwordTextBox.getText(), hrefList, 
        new AsyncCallback()
        {
          public void onSuccess(Object result) {
            callback.insert((ArrayList)result);
          }

          public void onFailure(Throwable caught) {
            if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
              log(PRCERRORNAME, "Cannot invoke method on servlet.");
            } 
            else {
              log(PRCERRORNAME, caught.toString());
            }
          }
        });
    } 
    else {
      callback.insert(hrefList);
    }
  }
  
  /***
   * This function sets up the next formatted event interval times.
   * The new time period for the query is derived & the timestamps are formatted for XML.
   */
  private void next() {
    this.prevPollTstamp = this.currPollTstamp;
    this.currPollTstamp = DateTimeFactories.makeTimestamp();
  }
  
  /**
   * This is the call back for the SDVTimer task. It polls the Servlet (which communicates with
   * the SensorBase).
   */
  public void poll() {
    if (this.rpcAvailable && this.hostStatus.booleanValue() && this.userStatus.booleanValue()) {
      this.rpcAvailable = false;
      next();
      /* this.timerPanelImage.setUrl(WAIT_IMAGE);
      this.timerPanelFeedback.setText("Requesting data from the Sensorbase at " 
        + this.timefactory.format(this.xmlfactory.parse(this.currEventTime)) + '.');
       */
      rpcGetMod(hostLabel.getText(), DEFAULT, this.userTextBox.getText(), 
        this.passwordTextBox.getText(), this.prevPollTstamp, this.currPollTstamp);
    }
  }
  
  /**
   * Log this message in the SensorDataViewer data display.
   * @param header the message header or subject
   * @param body the message body
   */
  public void log(String header, String body) {
    String bodytimestamp = DateTimeFactories.getInstance().getBodyfactory().format(
      DateTimeFactories.getInstance().getZonefactory().parse(this.currPollTstamp));
    TreeItem leaf = new TreeItem(bodytimestamp + " " + "Error" + " " + "SensorDataViewer");
    leaf.addItem("Timestamp=" + this.currPollTstamp);
    leaf.addItem("Resource=" + "SensorDataViewer Client");
    leaf.addItem("Host=" + this.hostLabel);
    leaf.addItem("User=" + this.userTextBox);
    leaf.addItem("Error:Name=" + header);
    leaf.addItem("Error:Message=" + body);
    this.dataTree.addItem(leaf);
    Window.alert(body);
  }
  
  /**
   * This method digests the response from the RPC call into local data structures.
   * @param dataCollection an ArrayList of ArrayLists of Strings containing the sensor data.
   * 
   * @gwt.typeArgs dataCollection <java.util.ArrayList<java.lang.String>>
   */ 
  public void work(ArrayList dataCollection) {
    this.serverTstamp = DateTimeFactories.getInstance().getXmlfactory().parse(this.currPollTstamp);
    String time = DateTimeFactories.getInstance().getTimefactory().format(this.serverTstamp);
    if (!dataCollection.isEmpty()) { //NOPMD Check for non-empty condition first.
      this.dataPanelImage.setUrl(WAIT_IMAGE);
      this.dataPanelFeedback.setText("Processing and displaying data for " + time);
      this.dataTree.addSensorData(dataCollection);
      this.lastArrivalTime = 
        DateTimeFactories.getInstance().getTimefactory().format(this.dataTree.getLastTstamp());
      this.lastCallTotal = this.dataTree.modSize();
      this.total += this.lastCallTotal;
      if (this.lastCallTotal == 1) {
        this.dataPanelFeedback.setHTML(time + ": 1 newly arrived Sensor Data instance, "
          + this.total + " total."
          + this.newLineChar + "The last sensor data timestamp is " + this.lastArrivalTime);
      } 
      else {
        this.dataPanelFeedback.setHTML(time + ": " + this.lastCallTotal
          + " newly arrived Sensor Data instances, " + this.total + " total."
          + this.newLineChar + "The last sensor data timestamp is " + this.lastArrivalTime); 
      }
    }
    else {
      this.dataPanelFeedback.setHTML(time + ": No newly arrived Sensor Data instances, "
          + this.total + " total."
          + this.newLineChar + "The last sensor data timestamp is " + this.lastArrivalTime); 
    }
    
    
    if (this.scrollPanel.isVisible()) {
      this.dataPanelImage.setUrl(OPEN_BOOK);
    } 
    else {
      this.dataPanelImage.setUrl(CLOSED_BOOK);
    }
    setHostStatus(Boolean.TRUE);
    this.rpcAvailable = true;
    resizeGadget();
  }
  
  /**
   * This method sets the state of the host uri validation.
   * @param state Boolean represents the state of the user valid=true, invalid=false
   */
  public void setHostStatus(Boolean state) {
    if (state.booleanValue()) {
      this.hostStatus = Boolean.TRUE;
    } 
    else {
      this.hostStatus = Boolean.FALSE;
    }
    this.rpcAvailable = true;
    this.serverTstamp = new Date();
    displayStatus();
    if (state.booleanValue() && this.firstCalInit) {
      rpcIsUser();
    }
  }
  
  /**
   * This method sets the state of the user & key validation.
   * @param state Boolean represents the state of the user valid=true, invalid=false
   */
  public void setUserStatus(Boolean state) {
    if (state.booleanValue()) {
      this.userStatus = Boolean.TRUE;
      Cookies.setCookie("user", this.userTextBox.getText(), this.cookieExpires);
      Cookies.setCookie("password", this.passwordTextBox.getText(), this.cookieExpires);
    } 
    else {
      this.userStatus = Boolean.FALSE;
    }
    displayStatus();
    this.rpcAvailable = true;
    if (state.booleanValue() && this.firstCalInit) {
      this.firstCalInit = false;
      this.compartments.showStack(3);
      poll();
    }
  }
  
  /**
   * This method sets the state of the host uri validation.
   * @param state Boolean represents the state of the user valid=true, invalid=false
   */
  public void setRegisterUserStatus(Boolean state) {
    //this.serverTstamp = new Date();
    //this.userStatus = state;
    //displayStatus();
    if (state.booleanValue()) {
      this.userPanelImage.setUrl(USER_IMAGE);
      this.userPanelFeedback.setText("Registration requested for " + this.userTextBox.getText() 
        + ". Please check your e-mail for the response.");
      this.userPanelFeedback.setStyleName(VALIDLABELSTYLE);
    }
    else {
      this.userPanelImage.setUrl(USER_INVALID);
      this.userPanelFeedback.setText("Unable to request registration for " 
          + this.userTextBox.getText());
      this.userPanelFeedback.setStyleName(INVALIDLABELSTYLE);
    }
    this.rpcAvailable = true;
  }
  
  /**
   * This method sets the correct panel Images from the host and user status.
   */
  private void displayStatus() {
    String timestamp = DateTimeFactories.getInstance().getBodyfactory().format(this.serverTstamp);
    if (this.hostStatus.booleanValue()) {
      this.hostPanelImage.setUrl(HOST_VALID);
      this.hostPanelFeedback.setText(timestamp + ", Server contacted successfully.");
      this.hostPanelFeedback.setStyleName(VALIDLABELSTYLE);
      if (this.userStatus.booleanValue()) {
        this.userPanelImage.setUrl(USER_VALID);
        this.userPanelFeedback.setText("User " + this.userTextBox.getText() + " authorized.");
        this.userPanelFeedback.setStyleName(VALIDLABELSTYLE);
      } 
      else {
        this.userPanelImage.setUrl(USER_INVALID);
        this.userPanelFeedback.setText("User " + this.userTextBox.getText() 
          + " authorization failed.");
        this.userPanelFeedback.setStyleName(INVALIDLABELSTYLE);
      }
    } 
    else {
      this.hostPanelImage.setUrl(HOST_INVALID);
      this.hostPanelFeedback.setText(timestamp + ", Server unavailable.");
      this.hostPanelFeedback.setStyleName(INVALIDLABELSTYLE);
      this.userPanelImage.setUrl(USER_IMAGE);
      this.userPanelFeedback.setText("Set a valid host, before entering user credentials.");
      this.userPanelFeedback.setStyleName(EXAMPLELABELSTYLE);
    }
  }
  
  /**
   * Check with the servlet to see if the host is valid.
   */
  private void rpcIsHost() {
    if (this.rpcAvailable) {
      this.rpcAvailable = false;
      this.hostPanelImage.setUrl(WAIT_IMAGE);
      RPCServiceFactory.getInstance().getService().isHost(this.hostLabel.getText(),
        new AsyncCallback()
        {
          public void onSuccess(Object result) {
            setHostStatus((Boolean) result);
          }
      
          public void onFailure(Throwable caught) {
            if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
              log(PRCERRORNAME, "Cannot invoke host validation on servlet.");
            } 
            else {
              log(PRCERRORNAME, caught.toString());
            }
          }
        });
    }
  }
  
  /**
   * Check with the servlet to see if the user/password is valid.
   */
  private void rpcIsUser() {
    if (this.rpcAvailable) {
      this.rpcAvailable = false;
      this.userPanelImage.setUrl(WAIT_IMAGE);
      RPCServiceFactory.getInstance().getService().isRegistered(this.hostLabel.getText(), 
        this.userTextBox.getText(), this.passwordTextBox.getText(), new AsyncCallback()
        {
          public void onSuccess(Object result) {
            setUserStatus((Boolean) result);
          }

          public void onFailure(Throwable caught) {
            if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
              log(PRCERRORNAME, "Cannot invoke user validation on servlet.");
            } 
            else {
              log(PRCERRORNAME, caught.toString());
            }
          }
        });
    }
  }  
  
  /**
   * Check with the servlet to see if the user/password is valid.
   */
  private void rpcRegisterUser() {
    if (this.rpcAvailable) {
      this.rpcAvailable = false;
      this.userPanelImage.setUrl(WAIT_IMAGE);
      RPCServiceFactory.getInstance().getService().registerUser(this.hostLabel.getText(), 
        this.userTextBox.getText(), new AsyncCallback()
        {
          public void onSuccess(Object result) {
            setRegisterUserStatus((Boolean) result);
          }

          public void onFailure(Throwable caught) {
            if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
              log(PRCERRORNAME, "Cannot invoke user validation on servlet.");
            } 
            else {
              log(PRCERRORNAME, caught.toString());
            }
          }
        });
    }
  }  
  
  /**
   * This method is triggered when the UI changes. If the Timer field was changed, 
   * it updates the Timer task.
   * @param sender the UI element that received an event.
   */ 
  public void onChange(Widget sender) {
    Integer interval = new Integer(this.pollingBox.getValue(this.pollingBox.getSelectedIndex()));
    poller.scheduleRepeating(interval.intValue() * 1000);
    this.timerPanelFeedback.setText("Polling approximately every " 
        + this.pollingBox.getValue(this.pollingBox.getSelectedIndex()) + " seconds.");
    Cookies.setCookie("interval", String.valueOf(this.pollingBox.getSelectedIndex()), 
        this.cookieExpires);
  }
  
  /**
   * Nothing happens when the settings fields are given focus.
   * @param sender the UI widget that was given focus
   */
  public void onFocus(Widget sender) {
    /* nothing to do here */
  }

  /** 
   * When any of the settings fields lose focus then check to see if the information is valid.
   * @param sender the UI widget that is losing focus.
   */
  public void onLostFocus(Widget sender) {
    if (sender.equals(this.hostLabel)) {
      rpcIsHost();
    } 
    else if ((sender.equals(this.userTextBox)) || (sender.equals(this.passwordTextBox))) {
      rpcIsUser();
    }
  }
  
  /** 
   * Listens for clicks on the UI elements.
   * @param sender the UI widget that is losing focus.
   */
  public void onClick(Widget sender) {
    if (sender.equals(this.dataPanelImage)) {
      Cookies.setCookie("data-visible", String.valueOf(this.scrollPanel.isVisible()),
          this.cookieExpires);
      if (this.scrollPanel.isVisible()) {
        this.scrollPanel.setVisible(false);
        this.dataPanelImage.setUrl(CLOSED_BOOK);
      } 
      else {
        this.scrollPanel.setVisible(true);
        this.dataPanelImage.setUrl(OPEN_BOOK);
      }
    } 
    else if (sender.equals(this.registerUserButton)) {
      rpcRegisterUser();
    }
  }
}
